/**
 * 原始 Javascript 数组是无类型的。 Nashorn 运行在 JavaScript 中使用 java 数组。
 *
 * int[] 数组的行为像一个真正的 java int 数组。 但当试图添加非整数的值的数组时，
 * Nashorn 会执行隐式类型转换。 字符串会自动转换为 int，这相当方便。
 */
var fun_array = function () {
    var IntArray = Java.type("int[]");

    var array = new IntArray(5);
    array[0] = 5;
    array[1] = 4;
    array[2] = 3;
    array[3] = 2;
    array[4] = 1;

    try {
        array[5] = 23;
    } catch (e) {
        print(e.message);  // Array index out of range: 5
    }

    array[0] = "17";
    print(array[0]);  // 17

    array[0] = "wrong type";
    print(array[0]);  // 0

    array[0] = "17.3";
    print(array[0]);  // 17
};

/**
 * 可以使用 java 的集合来代替数组。首先使用 Java.type 定义一个j ava 类型，而后根据需要创建一个实例。
 */
var fun_collection = function () {
    var ArrayList = Java.type('java.util.ArrayList');
    var list = new ArrayList();
    list.add('a');
    list.add('b');
    list.add('c');
    for each (var el in list) print(el);  // a, b, c
};

/**
 * 为了遍历集合和数组中的元素，Nashorn 引入了 for each 语句。
 * 这就像是 Java 的 for 循环一样。这里是一个对集合元素进行遍历的例子。
 */
var fun_foreach = function () {
    var map = new java.util.HashMap();
    map.put('foo', 'val1');
    map.put('bar', 'val2');
    for each (var e in map.keySet()) print(e);  // foo, bar
    for each (var e in map.values()) print(e);  // val1, val2
};

var fun_lambda_streams = function () {
    var list2 = new java.util.ArrayList();
    list2.add("ddd2");
    list2.add("aaa2");
    list2.add("bbb1");
    list2.add("aaa1");
    list2.add("bbb3");
    list2.add("ccc");
    list2.add("bbb2");
    list2.add("ddd1");

    list2
        .stream()
        .filter(function (el) {
            return el.startsWith("aaa");
        })
        .sorted()
        .forEach(function (el) {
            print(el);
        });
    // aaa1, aaa2
};

/**
 * Java 的类型可以简单的通过 Java.extend 进行扩展，在下面将在脚本中创建一个多线程示例
 */
var fun_type_extend = function () {
    var Runnable = Java.type('java.lang.Runnable');
    var Printer = Java.extend(Runnable, {
        run: function() {
            print('printed from a separate thread');
        }
    });

    var Thread = Java.type('java.lang.Thread');
    new Thread(new Printer()).start();

    new Thread(function() {
        print('printed from another thread');
    }).start();

    // printed from a separate thread
    // printed from another thread
};

/**
 * 方法和函数可以使用点符号或方括号来进行调用。
 * 在使用重载的参数来调用方法时可以传递可选参数来确定具体调用了哪个方法，如 println(double)。
 */
var fun_param_override = function () {
    var System = Java.type('java.lang.System');
    System.out.println(10);              // 10
    System.out["println"](11.0);         // 11.0
    System.out["println(double)"](12);   // 12.0
};

/**
 * 不需要常规的用 getter 或者 setter 来访问类成员属性，可直接用属性名简单访问 Java Bean 中的属性。
 */
var fun_java_bean = function () {
    var Date = Java.type('java.util.Date');
    var date = new Date();
    date.year += 1900;
    print(date.year);  // 2014
};

/**
 * 只是简单的一行函数我们可以不用大括号
 */
function sqr(x) x * x;

/**
 * 来自不同对象的属性可以绑定在一起。
 */
var fun_binding = function () {
    var o1 = {};
    var o2 = { foo: 'bar'};

    Object.bindProperties(o1, o2);

    print(o1.foo);    // bar
    o1.foo = 'BAM';
    print(o2.foo);    // BAM
};

/**
 * 字符串处理。
 */
var fun_deal_string = function () {
    print("   hehe".trimLeft());            // hehe
    print("hehe    ".trimRight() + "he");   // hehehe
};

var fun_where = function () {
    print(__FILE__, __LINE__, __DIR__);
};

/**
 * 有时，这在一次性导入多个java 包时非常有用。我们可以使用JavaImporter并结合with，在with块范围内引用。
 */
var fun_import = function () {
    var imports = new JavaImporter(java.io, java.lang);
    with (imports) {
        var file = new File(__FILE__);
        System.out.println(file.getAbsolutePath());
        // /path/to/my/script.js
    }
};

/**
 * 有些包时可以直接使用而不必利用 Java.type 或JavaImporter引入，如 java.util。
 */
var fun_array_convert = function () {
    var list = new java.util.ArrayList();
    list.add("s1");
    list.add("s2");
    list.add("s3");

    // 将java list转换为JavaScript的数组
    var jsArray = Java.from(list);
    print(jsArray);                                  // s1,s2,s3
    print(Object.prototype.toString.call(jsArray));  // [object Array]

    // 其他的方式
    var javaArray = Java.to([3, 5, 7, 11], "int[]");
    print(javaArray)
};

/**
 * 有些包时可以直接使用而不必利用 Java.type 或JavaImporter引入，如 java.util。
 */
var fun_array_convert = function () {
    var list = new java.util.ArrayList();
    list.add("s1");
    list.add("s2");
    list.add("s3");

    // 将java list转换为JavaScript的数组
    var jsArray = Java.from(list);
    print(jsArray);                                  // s1,s2,s3
    print(Object.prototype.toString.call(jsArray));  // [object Array]

    // 其他的方式
    var javaArray = Java.to([3, 5, 7, 11], "int[]");
    print(javaArray)
};

/**
 * 调用 父类函数
 */
var fun_super_method = function () {
    var SuperRunner = Java.type('com.lpf.nashorn.SuperRunner');
    var Runner = Java.extend(SuperRunner);

    var runner = new Runner() {
        run: function() {
            Java.super(runner).run();
            print('on my run');
        }
    }
    runner.run();
    // super run
    // on my run
};

/**
 * 在JavaScript中执行其它脚本是十分容易的。可以load函数载入本地或远程的脚本。
 */
var fun_remote_js = function () {
    load('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js');
    /*
     * 扩展脚本的执行是在同一个 JavaScript 上下文中，因此可以直接访问 underscore 变量。脚本的加载可能会因为变量名的重叠导致代码出问题。
     * 可以通过将加载的脚本文件放置到一个新的全局上下文来解决这个问题：
     */
    // loadWithNewGlobal('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js');

    var odds = _.filter([1, 2, 3, 4, 5, 6], function (num) {
        return num % 2 === 1;
    });

    print(odds);  // 1, 3, 5
};